﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using BrainTechLLC;
using ImageAppDataModels;
using ImageAppViewModels;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using BrainTechLLC.ThreadSafeObjects;
using BinaryNorthwest;

namespace WpfImageDuplicates
{
    public class FileImageViewModelCollection : ObservableCollection<FileImageViewModel>
    {
        public object _lock = new object();
        protected bool _SuspendCollectionChanged;
        public ThreadSafeList<NotifyCollectionChangedEventArgs> _SavedEvents;
        protected ThreadSafeLookupNonRef<string, int> _Index;
        public bool IndexDirty;

        public ThreadSafeLookupNonRef<string, int> Index
        {
            get
            {
                if (_Index == null || IndexDirty)
                {
                    lock (_lock)
                    {
                        if (_Index == null || IndexDirty)
                        {
                            ThreadSafeLookupNonRef<string, int> index = new ThreadSafeLookupNonRef<string, int>();
                            for (int n = 0; n < this.Count; n++)
                            {
                                var vm = this[n];
                                if (vm != null)
                                {
                                    index.Add(vm.FileName, n);
                                }
                            }
                            _Index = index;
                            IndexDirty = false;
                        }
                    }
                }

                return _Index;
            }
        }

        public bool SuspendCollectionChanged
        {
            get { return _SuspendCollectionChanged; }
            set
            {
                if (_SuspendCollectionChanged != value)
                {
                    _SuspendCollectionChanged = value;
                    if (value)
                    {
                        _SavedEvents = new ThreadSafeList<NotifyCollectionChangedEventArgs>();
                    }
                    else
                    {
                        var all = _SavedEvents.AllItems;
                        all.ForEach(o => OnCollectionChanged(o));
                        _SavedEvents = null;
                    }
                }
            }
        }

        protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            base.OnCollectionChanged(e);
        }

        public void AddItem(FileImageViewModel vm)
        {
            if (this.Count == 0)
            {
                lock (_lock)
                {
                    Index.Add(vm.FileName, 0);
                    vm.SetDesiredOrderingSilent(1);
                    base.Add(vm);
                }
                return;
            }

            SortPropOrFieldAndDirection fdir = new SortPropOrFieldAndDirection(Settings.Values.SortBy, Settings.Values.SortDescending);
            SortPropOrFieldAndDirection fdir2 = new SortPropOrFieldAndDirection(string.IsNullOrEmpty(Settings.Values.SortBy2) ? "FileName" : Settings.Values.SortBy2, Settings.Values.SortDescending2);
            var comparer1 = fdir.GetComparer<FileImageViewModel>();
            var comparer2 = fdir2.GetComparer<FileImageViewModel>();
            int moveLength = Math.Max(1, (this.Count / 2) + 1);
            int location = 0;
            int direction = 1;
            int lastDirection = 0;

            //Console.WriteLine(vm.FileDateTime.ToString());

            if (this.Count == 1)
            {
                FileImageViewModel comparison = this[0];
                int result1 = comparer1.Compare(vm, comparison);
                if (result1 > 0)
                {
                    lock (_lock)
                    {
                        Index.Add(vm.FileName, 1);
                        vm.SetDesiredOrderingSilent(2);
                        base.Add(vm);
                    }
                }
                else
                {
                    lock (_lock)
                    {
                        location = Index.Increment(comparison.FileName);
                        Index.Add(vm.FileName, 0);
                        vm.SetDesiredOrderingSilent(1);
                        base.Insert(0, vm);
                    }
                }
                return;
            }

            while (moveLength != 0)
            {
                int newLocation = location + moveLength;
                if (newLocation >= this.Count)
                {
                    newLocation = Math.Max(0, this.Count - 1);
                }
                else if (newLocation < 0)
                {
                    newLocation = 0;
                }

                FileImageViewModel comparison = this[newLocation];
                int result1 = comparer1.Compare(vm, comparison);

                if (result1 == 0)
                {                    
                    int result2 = comparer2.Compare(vm, comparison);
                    if (result2 < 0)
                    {
                        direction = -1;
                        moveLength = -1;
                        if (lastDirection == 1)
                        {
                            break;
                        }
                        location = newLocation;
                        lastDirection = direction;
                    }
                    else if (result2 > 0)
                    {
                        direction = 1;
                        moveLength = 1;
                        if (lastDirection == -1)
                        {
                            break;
                        }
                        location = newLocation;
                        lastDirection = direction;
                    }
                    else if (result2 == 0)
                    {
                        direction = 0;
                        moveLength = 0;
                        location = newLocation;
                        break;
                    }
                }
                else if (lastDirection != 0)
                {
                    break;
                }
                else if (result1 > 0 && direction == -1)
                {
                    direction = 1;
                    location = ((location + newLocation) / 2) - moveLength;
                }
                else if (result1 < 0 && direction == 1)
                {
                    direction = -1;
                    location = ((location + newLocation) / 2) - moveLength;
                }
                else if (result1 > 0 && direction == 1)
                {
                    location = newLocation;
                    moveLength = Math.Abs(moveLength / 2);
                }
                else if (result1 < 0 && direction == -1)
                {
                    location = newLocation;
                    moveLength = 0 - (Math.Abs(moveLength / 2));
                }
            }

            lock (_lock)
            {
                if (location >= this.Count)
                {
                    location = Math.Max(0, this.Count - 1);
                }
                else if (location < 0)
                {
                    location = 0;
                }
                FileImageViewModel comparison = this[location];
                int result1 = comparer1.Compare(vm, comparison);
                if (result1 > 0)
                {
                    location++;
                }
                else if (result1 < 0)
                {
                    location--;
                }
                if (location == this.Count)
                {
                    int count = this.Count;
                    Index.Add(vm.FileName, count);
                    vm.SetDesiredOrderingSilent(count + 2);
                    base.Add(vm);
                }
                else
                {
                    var insertLoc = Math.Min(this.Count - 1, Math.Max(0, location));
                    base.Insert(insertLoc, vm);
                    Index.Add(vm.FileName, insertLoc);
                    vm.SetDesiredOrderingSilent(insertLoc + 1);

                    //if (insertLoc < 50)
                    //{
                    for (int n = insertLoc + 1; n < this.Count; n++)
                    {
                        var idx = Index.Increment(this[n].FileName);
                        this[n].SetDesiredOrderingSilent(idx + 1);
                    }                    
                    //}                    
                }
                //Console.WriteLine(this.BuildList(o => o.FileDateTime.ToString()));
            }            
        }

        public void AddItemAt(FileImageViewModel vm, int idx)
        {
            lock (_lock)
            {
                Index.Add(vm.FileName, idx);
                base.Add(vm);
            }
        }

        public void RemoveItem(FileImageViewModel vm)
        {
            lock (_lock)
            {
                base.Remove(vm);
                IndexDirty = true;
            }
        }

        public int GetIndex(string fileName)
        {
            int idx;
            if (Index.TryGetValue(fileName, out idx))
            {
                return idx;
            }
            return -1;
        }
    }

}
